home *** CD-ROM | disk | FTP | other *** search
/ Collection of Internet / Collection of Internet.iso / winprogs / txtsrv / txtsrv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-25  |  17.4 KB  |  589 lines

  1. //
  2. // Text Server Version 1.0, a Windows Sockets Server
  3. //
  4. // Copyright 1993, Lee Murach
  5. //
  6. // Permission to use, modify, and distribute this software and its
  7. // documentation for any purpose and without fee is hereby granted,
  8. // provided that the above copyright notice appears in all copies and
  9. // that both that copyright notice and this permission notice appear in
  10. // supporting documentation.  Lee makes no claims as to the suitability
  11. // of this software for any purpose.
  12. //
  13. // Module TXTSRV, the only module of the Text Server, (TS) is both the
  14. // user interface and the network interface.  Since TS is a server, its
  15. // 'users' are TS clients, so the user interface requirements are
  16. // rather simple. TS simply updates its display with outbound buffer
  17. // traffic.  We also display winsock errors.
  18. //
  19. // TS is not a concurrent server; it processes one client request at a
  20. // time.  This simplifies the design considerably.  TS uses
  21. // WSAAsyncSelect() to receive the messages that notify of pending
  22. // client requests, and prompt TS to deliver a reply.  However, TS
  23. // switches to synchronous operation when retrieving a request, or
  24. // delivering a reply.  TS reenables request notification (FD_ACCEPT)
  25. // once it has finished its reply.  Meanwhile, other connection
  26. // requests remain in the listen() queue.
  27. //
  28. // TS speaks the finger protocol, and will reply to finger (e.g., the
  29. // winsock finger 3.x client).  We take the lazy way out, and assume
  30. // the text file has <CR><LF> terminators. (as DOS files do)
  31. //
  32. // 3/26/93  Lee Murach     wrote this thing.
  33. //
  34.  
  35. #include <windows.h>
  36. #include <string.h>
  37. #include <stdlib.h>
  38. #include <stdio.h>
  39. #include "winsock.h"
  40. #include "txtsrv.h"
  41.  
  42. #define WSVERSION 0x101             // windows sockets version
  43. #define SERVERPORT 79               // server listens on this port
  44. #define DEFAULTFILE "default.txt"   // send this file for null queries
  45. #define REQLEN 255                  // max length of request string
  46. #define XFERBUFLEN 1024             // max # of character for send/recv
  47. #define APIENTRY  PASCAL
  48. #define WNDPROC   FARPROC
  49.  
  50. typedef struct                      // associates messages (or menu ids) 
  51. {                                   // with a handler function
  52.    UINT Code;
  53.    LONG (*Fxn)(HWND, UINT, UINT, LONG);
  54. } DECODEWORD;
  55.  
  56. typedef struct                      // associates an error code with text
  57. {
  58.    UINT err;
  59.    char *sztext;
  60. } ERRENTRY;
  61.  
  62. #define dim(x) (sizeof(x) / sizeof(x[0]))
  63.  
  64. HWND hFrame;                        // handle of main window
  65. HANDLE hInst;                       // our instance
  66. char szAppName[] = "Text Server";   // the server named text
  67. SOCKET ListenSocket;                // awaits connection requests
  68. SOCKET ConnectSocket;               // is connected to client
  69. char Request[REQLEN + 1];           // holds request from client
  70. int ReqLen;                         // length of request string
  71. char Buf[XFERBUFLEN];               // transfer buffer holds in/outbound text
  72. int BufLen = 0;                     // # of chars in xfer buffer
  73.  
  74. BOOL InitApp(HANDLE hInstance);
  75. BOOL InitInstance(HANDLE hInstance, int nCmdShow);
  76. BOOL FAR APIENTRY AboutDlgProc(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam);
  77. LONG FAR APIENTRY FrameWndProc(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam);
  78. LONG DoMenuAbout(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam);
  79. LONG DoPaint(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam);
  80. LONG DoCommand(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam);
  81. LONG DoListen(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam);
  82. LONG DoClose(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam);
  83. LONG DoDestroy(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam);
  84. LONG DoConnection(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam);
  85. LONG DoGetRequest(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam);
  86. LONG DoReply(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam);
  87. void SendError(char *errstr);
  88. void SendFile(FILE *infile);
  89. void Blocking(SOCKET s);
  90. void DebugOut(char FAR *lps, ...);
  91. void DisplayWSError(void);
  92. void CloseSockets(void);
  93. void UpdateDisplay(int len);
  94. void SizeWindow();
  95. char *WSErrorString(UINT err);
  96.  
  97. #define WM_LISTEN       (WM_USER + 1)  // listen for connections
  98. #define WM_CONNECTION   (WM_USER + 2)  // connection request awaits
  99. #define WM_GETREQUEST   (WM_USER + 3)  // request is waiting
  100. #define WM_REPLY        (WM_USER + 4)  // start reply
  101.  
  102. DECODEWORD frameMsgs[] =               // windows messages & handlers
  103. {
  104.    WM_LISTEN,     DoListen,
  105.    WM_CONNECTION, DoConnection,
  106.    WM_GETREQUEST, DoGetRequest,
  107.    WM_REPLY,      DoReply,
  108.    WM_COMMAND,    DoCommand,
  109.    WM_PAINT,      DoPaint,
  110.    WM_CLOSE,      DoClose,
  111.    WM_DESTROY,    DoDestroy
  112. };
  113.  
  114. DECODEWORD menuItems[] =            // menu items & associated handlers
  115. {
  116.    IDM_ABOUT,     DoMenuAbout,
  117.    0,             0
  118. };
  119.  
  120. ERRENTRY WSErrors[] =               // error text for windows sockets errors
  121. {
  122.    WSAVERNOTSUPPORTED,  "This version of Windows Sockets is not supported",
  123.    WSASYSNOTREADY,      "Windows Sockets is not present or is not responding",
  124. };
  125.  
  126. //
  127. // WinMain -- Windows calls this to start the application.
  128. //
  129. int APIENTRY WinMain(HANDLE hInstance, HANDLE hPrevInstance,
  130.                      LPSTR lpCmdLine, int nCmdShow)
  131. {
  132.    WSADATA WSAData;                             // windows sockets info return
  133.    MSG msg;                                  // holds current message
  134.    int err;
  135.  
  136.    hInst = hInstance;                        // save the instance handle
  137.    if (hPrevInstance)                        // only one server, thank you
  138.       return(FALSE);
  139.  
  140.    if (!(InitApp(hInstance) && InitInstance(hInstance, nCmdShow)))
  141.       return(FALSE);                         // can't start, so bail
  142.  
  143.    if (err = WSAStartup(WSVERSION, &WSAData))// connect to winsock
  144.    {
  145.       MessageBox(hFrame, WSErrorString(err), szAppName, MB_ICONSTOP | MB_OK);
  146.       DestroyWindow(hFrame);                 // kill application window &
  147.    }                                         // signal app exit
  148.    else
  149.       PostMessage(hFrame, WM_LISTEN, 0, 0);  // get ready for clients
  150.    
  151.    while (GetMessage(&msg, NULL, 0, 0))      // loop til WM_QUIT
  152.    {
  153.       TranslateMessage(&msg);
  154.       DispatchMessage(&msg);
  155.    }
  156.  
  157.    WSACleanup();                             // disconnect from winsock
  158.    return(msg.wParam);                       // return to windows
  159. }
  160.  
  161. //
  162. // InitApp -- initialization for all instances of application.
  163. // This registers the main window class.
  164. //
  165. BOOL InitApp(HANDLE hInstance)
  166. {
  167.    WNDCLASS    wndclass;
  168.  
  169.    wndclass.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
  170.    wndclass.lpfnWndProc   = FrameWndProc;
  171.    wndclass.cbClsExtra    = 0;
  172.    wndclass.cbWndExtra    = 0;
  173.    wndclass.hInstance     = hInstance;
  174.    wndclass.hIcon         = LoadIcon(hInst, "Icon");
  175.    wndclass.hCursor       = LoadCursor(NULL, IDC_ARROW);
  176.    wndclass.hbrBackground = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
  177.    wndclass.lpszMenuName  = "Menu";
  178.    wndclass.lpszClassName = szAppName;
  179.  
  180.    return(RegisterClass(&wndclass));
  181. }
  182.  
  183. //
  184. // InitInstance -- initializes this instance of app, creates windows.
  185. //
  186. BOOL InitInstance(HANDLE hInstance, int nCmdShow)
  187. {
  188.    hFrame = CreateWindow(  szAppName, szAppName,
  189.                            WS_OVERLAPPEDWINDOW,
  190.                            CW_USEDEFAULT, CW_USEDEFAULT,
  191.                            CW_USEDEFAULT, CW_USEDEFAULT,
  192.                            NULL, NULL, hInstance, NULL);
  193.    if (!hFrame)
  194.       return(FALSE);
  195.  
  196.    SizeWindow();                             // set window & char sizes
  197.    ShowWindow(hFrame, nCmdShow);
  198.    UpdateWindow(hFrame);
  199.  
  200.    return(TRUE);                             // connections
  201. }
  202.  
  203. //
  204. // SizeWindow -- sets window's character and external dimensions.
  205. //
  206. void SizeWindow()
  207. {
  208.    HDC hdc;
  209.    TEXTMETRIC tm;
  210.    RECT rect;
  211.    int ychar, xchar;
  212.  
  213.    hdc = GetDC(hFrame);
  214.    SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
  215.    GetTextMetrics(hdc, &tm);
  216.    ychar = tm.tmHeight + tm.tmExternalLeading;
  217.    xchar = tm.tmAveCharWidth;
  218.    ReleaseDC(hFrame, hdc);
  219.  
  220.    // set initial window width & height in chars
  221.    GetWindowRect(hFrame, &rect);
  222.    MoveWindow( hFrame, rect.left, rect.top,
  223.                80 * xchar,
  224.                24 * ychar + GetSystemMetrics(SM_CYCAPTION) +
  225.                GetSystemMetrics(SM_CYMENU), FALSE);
  226. }
  227.  
  228. //
  229. // FrameWndProc -- callback function for application frame (main) window.
  230. // Decodes message and routes to appropriate message handler. If no handler
  231. // found, calls DefWindowProc.
  232. // 
  233. LONG FAR APIENTRY FrameWndProc(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  234. {
  235.    int i;
  236.  
  237.    for (i = 0; i < dim(frameMsgs); i++)
  238.    {
  239.       if (wMsg == frameMsgs[i].Code)
  240.          return(*frameMsgs[i].Fxn)(hWnd, wMsg, wParam, lParam);
  241.    }
  242.  
  243.    return(DefWindowProc(hWnd, wMsg, wParam, lParam));
  244. }
  245.  
  246. //
  247. // DoCommand -- demultiplexes WM_COMMAND messages resulting from menu
  248. // selections, and routes to corresponding menu item handler.  Sends back
  249. // any unrecognized messages to windows.
  250. //
  251. LONG DoCommand(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  252. {
  253.    int i;
  254.  
  255.    for (i = 0; i < dim(menuItems); i++)
  256.    {
  257.       if (wParam == menuItems[i].Code)
  258.          return(*menuItems[i].Fxn)(hWnd, wMsg, wParam, lParam);
  259.    }
  260.  
  261.    return(DefWindowProc(hWnd, wMsg, wParam, lParam));
  262. }
  263.  
  264. //
  265. // DoClose -- responds to close message by refusing pending client connects
  266. // and terminating connections in progress, then kills window.
  267. //
  268. LONG DoClose(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  269. {
  270.    CloseSockets();
  271.  
  272.    DestroyWindow(hWnd);
  273.    return(FALSE);
  274. }
  275.  
  276. //
  277. // DoDestroy -- posts a WM_QUIT message to the task's win queue, which
  278. // causes the main translate & dispatch loop to exit, and the app to
  279. // terminate.
  280. //
  281. LONG DoDestroy(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  282. {
  283.    PostQuitMessage(0);
  284.    return(FALSE);
  285. }
  286.  
  287. //
  288. // DoPaint -- Paint the client window with the contents of the
  289. // transfer buffer.
  290. //
  291. LONG DoPaint(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  292. {
  293.    RECT rect;                 // client window dimensions
  294.    HDC hdc;
  295.    PAINTSTRUCT ps;
  296.  
  297.    hdc = BeginPaint(hWnd, &ps);
  298.    GetClientRect(hWnd, &rect);
  299.    DrawText(hdc, Buf, BufLen, &rect, DT_EXPANDTABS);
  300.    EndPaint(hWnd, &ps);
  301.  
  302.    return(FALSE);
  303. }
  304.  
  305. //
  306. // DoMenuAbout -- respond to "About..." menu selection by invoking the
  307. // "About" dialog box.
  308. //
  309. LONG DoMenuAbout(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  310. {
  311.    WNDPROC lpProcAbout;
  312.  
  313.    lpProcAbout = MakeProcInstance((WNDPROC)AboutDlgProc, hInst);
  314.    DialogBox(hInst, "AboutBox", hWnd, lpProcAbout);
  315.    FreeProcInstance(lpProcAbout);
  316.  
  317.    return(FALSE);
  318. }
  319.  
  320. //
  321. // AboutDlgProc -- callback for the "About" dialog box
  322. //
  323. BOOL FAR APIENTRY AboutDlgProc(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  324. {
  325.    if ((wMsg == WM_COMMAND) && (wParam == IDOK))   // dismiss dialog if OK
  326.       EndDialog(hWnd, 0);
  327.  
  328.    return(FALSE);                                  // otherwise just sit there
  329. }
  330.  
  331. //
  332. // UpdateDisplay -- display is xfer buffer, so we store the size, and
  333. // force a repaint.
  334. //
  335. void UpdateDisplay(int len)
  336. {
  337.    BufLen = len;
  338.    InvalidateRect(hFrame, NULL, TRUE);
  339. }
  340.  
  341. //
  342. // DoListen -- set up a listen socket to listen for client connection
  343. // requests.  The WM_CONNECTION will signal that such is pending.
  344. //
  345. LONG DoListen(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  346. {
  347.    SOCKADDR_IN sin;
  348.  
  349.    ListenSocket = ConnectSocket = INVALID_SOCKET;
  350.  
  351.    if ((ListenSocket = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
  352.    {
  353.       DisplayWSError();
  354.       return(FALSE);
  355.    }
  356.  
  357.    sin.sin_family = AF_INET;
  358.    sin.sin_addr.s_addr = INADDR_ANY;
  359.    sin.sin_port = htons(SERVERPORT);
  360.  
  361.    if (bind(ListenSocket, (LPSOCKADDR) &sin, sizeof(sin)))
  362.    {
  363.       DisplayWSError();
  364.       return(FALSE);
  365.    }
  366.  
  367.    if (listen(ListenSocket, 5))
  368.    {
  369.       DisplayWSError();
  370.       return(FALSE);
  371.    }
  372.  
  373.    WSAAsyncSelect(ListenSocket, hFrame, WM_CONNECTION, FD_ACCEPT);
  374.    return(FALSE);
  375. }
  376.  
  377. //
  378. // DoConnection -- opens a connection to requesting client, and
  379. // signals interest in data for read.  We'll receive a WM_GETREQUEST
  380. // message when data has arrived.
  381. //
  382. LONG DoConnection(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  383. {
  384.    struct sockaddr sad;
  385.    int len = sizeof(sad);
  386.  
  387.    /* take connection request off queue, and disable further
  388.       notifications.  we can process only one request at a time. */
  389.  
  390.    ConnectSocket = accept(ListenSocket, &sad, &len);
  391.    WSAAsyncSelect(ListenSocket, hFrame, 0, 0);
  392.  
  393.    if (ConnectSocket == INVALID_SOCKET)
  394.    { 
  395.       DisplayWSError();
  396.       return(FALSE);
  397.    }
  398.  
  399.    // notify us when a request (or part of one) has arrived
  400.    WSAAsyncSelect(ConnectSocket, hFrame, WM_GETREQUEST, FD_READ);
  401.  
  402.    return(FALSE);
  403. }
  404.  
  405. //
  406. // CloseSockets -- shuts down listen, and any transfer in progress.
  407. //
  408. void CloseSockets(void)
  409. {
  410.    if (ListenSocket != INVALID_SOCKET);
  411.       closesocket(ListenSocket);
  412.    if (ConnectSocket != INVALID_SOCKET);
  413.       closesocket(ConnectSocket);
  414. }
  415.  
  416. //
  417. // DoGetRequest -- synchronously receives the client's query, and places
  418. // it in the request buffer.  We expect <CR><LF> terminator, but will accept
  419. // end-of-stream (zero recv return).  We then ask for a write-space-available
  420. // notification.
  421. //
  422. LONG DoGetRequest(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  423. {
  424.    char *p;
  425.    int nchars;
  426.  
  427.    Request[0] = ReqLen = 0;
  428.    Blocking(ConnectSocket);               // going synchronous now
  429.  
  430.    do // until we've received the whole request
  431.    {
  432.       nchars = recv(ConnectSocket, (LPSTR) Buf, sizeof(Buf), 0);
  433.  
  434.       if (nchars == SOCKET_ERROR)
  435.       {
  436.          DisplayWSError();
  437.          return(FALSE);
  438.       }
  439.  
  440.       Buf[nchars] = 0;                    // null terminate the buffer
  441.  
  442.       if ((ReqLen += nchars) > REQLEN)    // prevent request overflow
  443.       {
  444.          SendError("query too long");     // tell client
  445.          return(FALSE);                   // request overflow, bail
  446.       }
  447.  
  448.       strcat(Request, Buf);               // append to request
  449.       p = strstr(Request, "\r\n");        // end-of-request?
  450.  
  451.    }
  452.    while (nchars && !p);                  // stop for either end-of-stream
  453.                                           // or <CR><LF> terminator
  454.    if (p) *p = 0;                         // chop the terminator
  455.    
  456.    if (!Request[0])                       // translate null request to default
  457.       strcpy(Request, DEFAULTFILE);
  458.  
  459.    // going asychronous now, this is will notify us of when to reply
  460.    WSAAsyncSelect(ConnectSocket, hFrame, WM_REPLY, FD_WRITE);
  461.  
  462.    return(FALSE);
  463. }
  464.  
  465. //
  466. // DoReply -- synchronously replies to client with either the requested
  467. // file or an error text, then reenables accept notifies, so we can
  468. // process the next request.
  469. //
  470. LONG DoReply(HWND hWnd, UINT wMsg, UINT wParam, LONG lParam)
  471. {
  472.    FILE *infile;
  473.  
  474.    Blocking(ConnectSocket);               // going synchronous now
  475.  
  476.    if (infile = fopen(Request, "rb"))     // open for read, binary
  477.       SendFile(infile);
  478.    else
  479.       SendError(_strerror(NULL));         // file access error text
  480.  
  481.    fclose(infile);
  482.    closesocket(ConnectSocket);
  483.    ConnectSocket = INVALID_SOCKET;
  484.  
  485.    // we're ready for the next request now
  486.    WSAAsyncSelect(ListenSocket, hFrame, WM_CONNECTION, FD_ACCEPT);
  487.  
  488.    return(FALSE);
  489. }
  490.  
  491. //
  492. // Blocking -- sets socket to block.
  493. //
  494. void Blocking(SOCKET s)
  495. {
  496.    u_long nonblock = FALSE;               // yes, we have no bananas
  497.  
  498.    WSAAsyncSelect(s, hFrame, 0, 0);       // turn off message notifications
  499.    ioctlsocket(s, FIONBIO, &nonblock);    // set socket to blocking
  500. }
  501.  
  502. //
  503. // SendFile -- sends the infile down the ConnectSocket
  504. // stream.
  505. //
  506. void SendFile(FILE *infile)
  507. {
  508.    int nchars;
  509.  
  510.    do
  511.    {
  512.       nchars = fread(Buf, 1, sizeof(Buf), infile);
  513.  
  514.       if (ferror(infile))
  515.       {
  516.          SendError(_strerror(NULL));
  517.          UpdateDisplay(0);
  518.          return;
  519.       }
  520.  
  521.       if (nchars > 0)
  522.       {
  523.          UpdateDisplay(nchars);     // buffer changed, so repaint
  524.    
  525.          if (send(ConnectSocket, Buf, nchars, 0) == SOCKET_ERROR)
  526.          {
  527.             DisplayWSError();
  528.             return;
  529.          }
  530.       }
  531.    }
  532.    while (!feof(infile));
  533. }
  534.    
  535. //
  536. // SendError -- sends an error string to client.
  537. //
  538. void SendError(char *errstr)
  539. {
  540.    static char *prefix = "\r\nserver error: ";
  541.  
  542.    send(ConnectSocket, prefix, strlen(prefix), 0);
  543.    send(ConnectSocket, errstr, strlen(errstr), 0);
  544.  
  545.    closesocket(ConnectSocket);
  546.    ConnectSocket = INVALID_SOCKET;
  547. }
  548.  
  549. //
  550. // DisplayWSError -- displays the winsock error in the client window.
  551. //
  552. void DisplayWSError(void)
  553. {
  554.    strcpy(Buf, WSErrorString(WSAGetLastError()));
  555.    UpdateDisplay(strlen(Buf));
  556.  
  557.    closesocket(ConnectSocket);
  558.    ConnectSocket = INVALID_SOCKET;
  559. }
  560.  
  561. //
  562. // WSErrorString -- translates winsock error to appropriate string.
  563. //
  564. char *WSErrorString(UINT err)
  565. {
  566.    int i;
  567.    static char szerr[80];
  568.  
  569.    for (i = 0; i < dim(WSErrors); i++)
  570.       if (err == WSErrors[i].err)
  571.          return(WSErrors[i].sztext);
  572.  
  573.    sprintf(szerr, "Windows Sockets reports error %04x", err);
  574.    return(szerr);
  575. }
  576.  
  577. //
  578. // DebugOut -- for outputting debug info to the AUX device (or debugger).
  579. //
  580. void DebugOut(char FAR *lps, ...)
  581. {
  582.    static char buf[80];
  583.    static char FAR *args;
  584.  
  585.    args = (char FAR *) &lps + sizeof(lps);
  586.    wvsprintf(buf, lps, args);
  587.    OutputDebugString(buf);
  588. }
  589.